#include <string.h>
#include <stdio.h>
#include "mc.h"
#include <netinet/in.h>
#include <binary_protocol.h>

int header_op=1;

void compose_binary_noop(char *buf) {
    binary_header_t h = {0x80, CMD_NOOP, htons(0),
                         0x00, 0x00, htons(0),
                         htonl(0)};
    if(header_op==1)
    {
        TCPheader tcph = {0x61, 0x00, htons(0), htons(1), htons(sizeof(TCPheader)+24+sizeof(h)), htonl(0)};
        TCPheader innh = {0x60, 0x00, htons(0), htons(1), htons(sizeof(h)), htonl(0)};
        memcpy(buf, &tcph, sizeof(tcph));
        memcpy(buf+sizeof(tcph), &innh, sizeof(innh));
        memcpy(buf + 24, &h, 24);
    }
    else
    {
        memcpy(buf, &h, 24);
    }
}

void compose_binary_set(char *request, char *key, char *value, int *body_len) {
    size_t k_len = strlen(key);
    size_t v_len = strlen(value);
    uint64_t cas = 0;
    binary_set_req_header header = { 0x80, CMD_SET, htons(k_len),
                                     0x08, 0x00, {htons(0)},
                                     htonl((uint32_t)(k_len + 8 + v_len))};
    if(header_op==1)
    {
        TCPheader tcph = {0x61, 0x00, htons(0), htons(1), htons((uint32_t)(k_len + v_len + sizeof(header) + sizeof(TCPheader))), htonl(0)};
        TCPheader innh = {0x60, 0x00, htons(0), htons(1), htons((uint32_t)(k_len + sizeof(header) + v_len)), htonl(0)};
        memcpy(request , &tcph , sizeof(tcph));
        memcpy(request + sizeof(tcph), &innh, sizeof(innh));
        memcpy(request + 2 * sizeof(TCPheader), &header, sizeof(header));
        memcpy(request + 2 * sizeof(TCPheader) + sizeof(header), key, k_len);
        memcpy(request + 2 * sizeof(TCPheader) + sizeof(header) + k_len, value, v_len);
    }
    else
    {
        memcpy(request, &header, 32);
        memcpy(request + 32, key, k_len);
        memcpy(request + 32 + k_len, value, v_len);
        //    fprintf(stderr, "finish create_binary_set_request k=%s (%lu) v=%s (%lu) buf_len=%zu\n",
        //            key, k_len, value, v_len, strlen(request));

    }
     *body_len = k_len + v_len;
}

int compose_binary_get(char *request, char *key, int cmd, int ip_rank, int port) {
    uint16_t key_len = (uint16_t)strlen(key);
    //uint16_t _req_ip_map_addr = (uint16_t) (46543 + rank);
    //uint16_t _req_port = (uint16_t) (32242 + rank * 5);
    uint8_t hip = (uint8_t)(0x00ff & ip_rank);
    binary_get_req_header h = {0x80, cmd, htons(key_len),
                               0x00, 0x00, {htons(0)},
                               htonl(key_len),
                               htons(ip_rank), htons(port),
                               0x0000000000000000};
    if(header_op==1)
    {
        TCPheader tcph = {0x61, hip, htons(port), htons(1), htons((uint32_t)(key_len + sizeof(h) + sizeof(TCPheader))), htonl(0)};
        TCPheader innh = {0x60, hip, htons(port), htons(1), htons((uint32_t)(key_len + sizeof(h))), htonl(0)};
        memcpy(request, &tcph, sizeof(tcph));
        memcpy(request + sizeof(tcph), &innh, sizeof(innh));
        memcpy(request + 2 * sizeof(TCPheader), &h, sizeof(h));
        memcpy(request + 2 * sizeof(TCPheader) + sizeof(h), key, key_len);
        //  fprintf(stderr, "finish compose binary_get op=0x%02x sz=%d ip=%u port=%u\n",
        //          cmd, 24+key_len, ip_rank, port);
        return (48+key_len);
    }
    else
    {
        memcpy(request, &h, sizeof(h));
        memcpy(request + sizeof(h), key, key_len);
        //  fprintf(stderr, "finish compose binary_get op=0x%02x sz=%d ip=%u port=%u\n",
        //          cmd, 24+key_len, ip_rank, port);
        return (24+key_len);
    }
}

int compose_binary_bget(char *request, char *key, uint8_t seq_number) {
    int h_l=0;
    uint16_t key_len = (uint16_t)strlen(key);
    uint32_t ip_addr = 12345 + seq_number;
    uint16_t req_port = (uint16_t) (33333 + seq_number);
    binary_bget_req_header h = {0x80, CMD_BGET, htons(key_len),
                                0x08, 0x00, {htons(0)},
                                htonl(key_len + 8),
                                htons((uint16_t)ip_addr), htons(req_port),
                                0x0000000000000000,
                                htonl(ip_addr),
                                htons(req_port),
                                htons(seq_number)
                               };
    h_l = sizeof(h);
    if(header_op==1)
    {
        uint8_t hip = (uint8_t)(0x00ff & ip_addr);
        TCPheader tcph = {0x61, hip, htons(req_port), htons(1), htons((uint32_t)(h_l + key_len + sizeof(TCPheader))), htonl(0)};
        TCPheader innh = {0x60, hip, htons(req_port), htons(1), htons((uint32_t)(h_l + key_len)), htonl(0)};
        memcpy(request, &tcph, sizeof(tcph));
        memcpy(request + sizeof(tcph), &innh, sizeof(innh));
        memcpy(request + 2 * sizeof(TCPheader), &h, h_l);
        memcpy(request + h_l + 2 * sizeof(TCPheader), key, key_len);
    }
    else
    {
        memcpy(request, &h, h_l);
        memcpy(request + h_l, key, key_len);
    }

    //fprintf(stderr, "finish compose binary_get op=0x%02x sz_h=%d sz_k=%d ip=%u port=%u\n",
    //        CMD_BGET, h_l, key_len, ip_addr, req_port);
    int pp=0;
    for ( pp=0; pp<h_l; pp++) {
        if (0 == pp%4)
            fprintf(stderr, "\n =>");

        fprintf(stderr, " 0x%02x", request[pp]);
    }
    fprintf(stderr, "\n");
    if(header_op==1)
        return (h_l + key_len + 24);
    else
        return (h_l + key_len);
}

int binary_get_req(char *request, char *key, int cmd, int rank) {
    uint16_t key_len = (uint16_t)strlen(key);
    uint16_t _req_ip_map_addr = (uint16_t) (46543 + rank);
    uint16_t _req_port = (uint16_t) (32242 + rank * 5);
    binary_get_req_header h = {0x80, cmd, htons(key_len),
                               0x00, 0x00, {htons(0)},
                               htonl(key_len),
                               htons(_req_ip_map_addr), htons(_req_port),
                               0x0000000000000000};
    if(header_op==1)
    {
        uint8_t hip = (uint8_t)(0x00ff & _req_ip_map_addr);
        TCPheader tcph = {0x61, hip, htons(_req_port), htons(1), htons((uint32_t)(sizeof(TCPheader) + sizeof(h) + key_len)), htonl(0)};
        TCPheader innh = {0x60, hip, htons(_req_port), htons(1), htons((uint32_t)(sizeof(h) + key_len)), htonl(0)};
        memcpy(request, &tcph, sizeof(tcph));
        memcpy(request + sizeof(h), &innh, sizeof(innh));
        memcpy(request + 2 * sizeof(TCPheader), &h, sizeof(h));
        memcpy(request + 2 * sizeof(TCPheader) + sizeof(h), key, key_len);
        // fprintf(stderr, "finish compose binary_get op=0x%02x sz=%d ip=%u port=%u\n",
        //      cmd, 24+key_len, _req_ip_map_addr, _req_port);
        return (2 * sizeof(TCPheader) + sizeof(h) + key_len);
    }
    else
    {
        memcpy(request, &h, sizeof(h));
        memcpy(request + sizeof(h), key, key_len);
        // fprintf(stderr, "finish compose binary_get op=0x%02x sz=%d ip=%u port=%u\n",
        //      cmd, 24+key_len, _req_ip_map_addr, _req_port);
        return (sizeof(h) + key_len);
    }


}

size_t compose_binary_mget_req_h(char *request, int batch_sz, size_t bodylen) {
    binary_get_req_header header = {0x80, CMD_MGET, htons(bodylen),
                                    (uint8_t)batch_sz, 0x00, {htons(0)},
                                    htonl(bodylen),
                                    0x00000000,
                                    0x0000000000000000};
    if(header_op==1)
    {
        TCPheader tcph = {0x61, 0x00, htons(0), htons(1), htons((uint32_t)(sizeof(TCPheader) + sizeof(header))), htonl(0)};
        TCPheader innh = {0x60, 0x00, htons(0), htons(1), htons((uint32_t)(sizeof(header))), htonl(0)};
        memcpy(request, &tcph, sizeof(tcph));
        memcpy(request + sizeof(tcph), &innh, sizeof(innh));
        memcpy(request + 2 * sizeof(TCPheader), &header, sizeof(header));
        return 2 * sizeof(TCPheader) + sizeof(header);
    }
    else{
        memcpy(request, &header,  sizeof(header));
        return  sizeof(header);
    }

}

size_t binary_mget_req(char *request, char **keys, int batch_size) {
    int total_size = 24;
    int i=0;
    for ( i=0; i<batch_size; i++) {
        total_size += binary_get_req(request + total_size, keys[i], CMD_GETKQ, i);
    }

    uint16_t fake_key_len = total_size-24;
    //fill in the header
    binary_get_req_header header = {0x80, CMD_MGET, htons(fake_key_len),
                                    (uint8_t)batch_size, 0x00, {htons(0)},
                                    htonl(fake_key_len),
                                    0x00000000,
                                    0x0000000000000000};
    if(header_op==1)
    {
        TCPheader tcph = {0x61, 0x00, htons(0), htons(1), htons((uint32_t)(sizeof(TCPheader) + sizeof(header))), htonl(0)};
        TCPheader innh = {0x60, 0x00, htons(0), htons(1), htons((uint32_t)(sizeof(header))), htonl(0)};

        memcpy(request, &tcph, sizeof(tcph));
        memcpy(request + sizeof(tcph), &innh, sizeof(innh));
        memcpy(request + 2 * sizeof(TCPheader), &header, sizeof(header));
        // fprintf(stderr, "finish assemble mget request #request=%d body_len=%d\n",
        //       batch_size, total_size-24);
        return (size_t)total_size + 2 * sizeof(TCPheader);
    }
    else {
        memcpy(request, &header, sizeof(header));
        // fprintf(stderr, "finish assemble mget request #request=%d body_len=%d\n",
        //       batch_size, total_size-24);
        return (size_t)total_size;
    }
}

uint16_t get_ip_from_resp_opaque(char *h) {
    binary_header_t *ht = (binary_header_t *)h;
    return ntohs(ht->src_ip);
}

uint16_t get_port_from_resp_opaque(char *h) {
    binary_header_t *ht = (binary_header_t *)h;
    return ntohs(ht->src_port);
}

uint32_t get_sfd_from_opaque(char *h) {
    binary_header_t *ht = (binary_header_t *)h;
    int i=0;
    for ( i=0; i<24; i++) {
        if (0 == i%4)
            fprintf(stderr, "\n dump > ");
        fprintf(stderr, "0x%02x ", h[i]);
    }
    fprintf(stderr, "\n\n");
    return ntohl(ht->opaque);
}

void fill_ip_port_to_req_h(char *h, uint16_t ip, uint16_t port) {
    binary_header_t *ht = (binary_header_t *)h;
    ht->src_ip = htons(ip);
    ht->src_port = htons(port);
}

void fill_sfd_to_req_h(char *h, uint32_t sfd) {
    binary_header_t *ht = (binary_header_t *)h;
    ht->opaque = htonl(sfd);
}
